package com.nlp.trie.viterbi;

import java.applet.Applet;
import java.awt.Button;
import java.awt.Event;
import java.awt.Panel;
import java.awt.TextArea;
import java.awt.TextField;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.util.StringTokenizer;
import java.util.concurrent.TimeUnit;

/**
 * 维特比
 * @author ygsong.abcft
 *
 */
public class Viterbi extends Applet implements ActionListener, Runnable{

	/**
	 * 
	 */
	private static final long serialVersionUID = 1L;
	SymbolTable symtab;
	StateTable sttab;
	HMMDecoder myhmm = null;
	HMMCanvas canvas;
	Panel p;
	TextArea hmmdesc;
	TextField sentence;
	Button bstart, bskip;
	static final String initalHMM = "start: go(r,1.0) emit(start,1.0)\n"
			+ "f: emit(来,0.1) go(n,0.9) go(end,0.1)\n"
			+ "v: emit(来,0.4) emit(会,0.3) go(f,0.1) go(v,0.3) go(n,0.5) go(end,0.1)\n"
			+ "n: emit(会,0.1) go(f,0.5) go(v,0.3) go(end,0.2)\n"
			+ "r: emit(他,0.3) go(v,0.9) go(n,0.1)\n";
	
	final int sleepmillisec = 100;
	
	//setup hmm
	//success:true
	 boolean setupHMM(String s) {
		 myhmm = new HMMDecoder();
		 symtab = new SymbolTable();
		 sttab = new StateTable();
		 
		 State start = sttab.get("start");
		 State end = sttab.get("end");
		 myhmm.addStartState(start);
		 
		 boolean success = true;
		 StringTokenizer lines = new StringTokenizer(s, "\n");
		 while (lines.hasMoreElements()) {
			String line =  lines.nextToken();
			int i = line.indexOf(':');
			if (i == -1) {
				break;
			}
			State st0 = sttab.get(line.substring(0, i).trim());
			if (st0 != start && st0 != end) {
				myhmm.addNormalState(st0);
			}
			
			StringTokenizer tokenz = new StringTokenizer(line.substring(i+1), ", ");
			while (tokenz.hasMoreElements()) {
				String t = tokenz.nextToken().toLowerCase();
				if (t.startsWith("go(")) {
					State st1 = sttab.get(t.substring(3).trim());
					if (!tokenz.hasMoreTokens()) {
						success = false;
						break;
					}
					String n = tokenz.nextToken().replace(')',' ');
					double prob;
					try {
						prob = Double.valueOf(n).doubleValue();
					} catch (Exception e) {
						success = false;
						prob = 0.0;
					}
					st0.addLink(st1, prob);
				}else if (s.startsWith("emit(")) {
					Symbol sym = symtab.intern(t.substring(5).trim());
					if (!tokenz.hasMoreElements()) {
						success = false;
						break;
					}
					String n = tokenz.nextToken().replace(')', ' ');
					double prob;
					try {
						prob = Double.valueOf(n).doubleValue();
					} catch (Exception e) {
						success = false;
						prob = 0.0;
					}
					st0.addSymbol(sym, prob);
				}else {
					break;
				}
			}
			st0.normalize();
		}
		 
		 end.addSymbol(symtab.intern("end"), 1.0);
		 myhmm.addEndState(end);
		 return success;
	}
	/**
	 * success:true
	 * @return
	 */
	boolean setup() {
		if (!setupHMM(hmmdesc.getText())) {
			return false;
		}
		
		//initalize words
		SymbolList words = new SymbolList();
		StringTokenizer tokenz = new StringTokenizer(sentence.getText());
		words.add(symtab.intern("start"));
		while(tokenz.hasMoreTokens()) {
			words.add(symtab.intern(tokenz.nextToken()));
		}
		
		words.add(symtab.intern("end"));
		myhmm.initialize(words);
		canvas.setHmm(myhmm);
		return true;
	}
	
	@Override
	public void init() {
		canvas = new HMMCanvas();
		setLayout(new java.awt.BorderLayout());
		p = new Panel();
		sentence = new TextField("他 会 来", 20);
		bstart = new Button("start");
		bskip = new Button("auto");
		bstart.addActionListener(this);
		bskip.addActionListener(this);
		p.add(sentence);
		p.add(bstart);
		p.add(bskip);
		hmmdesc = new TextArea(initalHMM, 4, 20);
		add("North", canvas);
		add("Center",p);
		add("South",hmmdesc);
	}
	
	void setup_fallback(){
		State cow = sttab.get("cow");
		State duck = sttab.get("duck");
		State end = sttab.get("end");
		
		cow.addLink(cow, 0.5);
		cow.addLink(duck, 0.3);
		cow.addLink(end, 0.2);
		duck.addLink(cow, 0.3);
		duck.addLink(duck, 0.5);
		duck.addLink(end, 0.2);
		
		cow.addSymbol(symtab.intern("moo"), 0.9);
		cow.addSymbol(symtab.intern("helllo"), 0.1);
		duck.addSymbol(symtab.intern("quack"), 0.6);
		duck.addSymbol(symtab.intern("hello"),0.4);
	}
	
	@Override
	public void destroy() {
		remove(p);
		remove(canvas);
	}
	
	@Override
	protected void processEvent(java.awt.AWTEvent e) {
		if (e.getID() == Event.WINDOW_DESTROY) {
			System.exit(0);
		}
	}
	@Override
	public void run() {
		if (myhmm != null) {
			while (myhmm.proceed_decoding()) {
				canvas.repaint();
				try {
					TimeUnit.MICROSECONDS.sleep(sleepmillisec);
				} catch (Exception e) {
					e.printStackTrace();
				}
			}
			myhmm.backward();
			canvas.repaint();
			bstart.setLabel(" Start ");
			bstart.setEnabled(true);
			bskip.setEnabled(true);
			myhmm = null;
		}
	}

	@Override
	public void actionPerformed(ActionEvent e) {
		String label = e.getActionCommand();
		if (label.equalsIgnoreCase(" start ")) {
			if (!setup()) {
				return;
			}
			bstart.setLabel("Proceed");
			canvas.repaint();
		}else if (label.equalsIgnoreCase("proceed")) {
			if (!myhmm.proceed_decoding()) {
				myhmm.backward();
				bstart.setLabel(" Start ");
				myhmm = null;
			}
			canvas.repaint();
		}else if (label.equalsIgnoreCase("auto")) {
			if (myhmm == null) {
				if (!setup()) {
					return;
				}
			}
			
			bstart.setEnabled(false);
			bskip.setEnabled(false);
			Thread me = new Thread(this);
			me.setPriority(Thread.MIN_PRIORITY);
			me.start();
		}
	}

	public static void main(String[] args) {
		java.awt.Frame frame = new java.awt.Frame("Viterbi");
		Viterbi viterbi = new Viterbi();
		frame.add("Center", viterbi);
		frame.setSize(400,400);
		viterbi.init();
		viterbi.start();
	}
	public String getAppletInfo() {
		return "A Sample Viterbi Decoder Applet";
	}
}
